package wdstruct

import (
	"sync"
	"sync/atomic"
)

type deque struct {
	len  int32
	head int32
	tail int32
	data *sync.Map

	zerolock sync.RWMutex
	headlock sync.RWMutex
	taillock sync.RWMutex
}

func New_Deque() I_Deque_Hand {
	return &deque{
		data: &sync.Map{},
		tail: 1,
	}
}

func (d *deque) GetLength() int32 {
	return atomic.LoadInt32(&d.len)
}
func (d *deque) PushToHead(data interface{}) {
	d.zerolock.Lock()
	d.headlock.Lock()
	if atomic.LoadInt32(&d.len) == 0 {
		d.data.Store(d.head, data)
		atomic.AddInt32(&d.head, -1)
		atomic.AddInt32(&d.len, 1)
		d.headlock.Unlock()
		d.zerolock.Unlock()
		return
	}
	d.zerolock.Unlock()
	d.data.Store(d.head, data)
	atomic.AddInt32(&d.head, -1)
	atomic.AddInt32(&d.len, 1)
	d.headlock.Unlock()
}
func (d *deque) PushToTail(data interface{}) {
	d.zerolock.Lock()
	d.taillock.Lock()
	if atomic.LoadInt32(&d.len) == 0 {
		d.data.Store(d.tail, data)
		atomic.AddInt32(&d.tail, 1)
		atomic.AddInt32(&d.len, 1)
		d.taillock.Unlock()
		d.zerolock.Unlock()
		return
	}
	d.zerolock.Unlock()
	d.data.Store(d.tail, data)
	atomic.AddInt32(&d.tail, 1)
	atomic.AddInt32(&d.len, 1)
	d.taillock.Unlock()
}
func (d *deque) PullFromHead() interface{} {
	d.zerolock.Lock()
	d.headlock.Lock()
	if atomic.LoadInt32(&d.len) <= 0 {
		d.headlock.Unlock()
		d.zerolock.Unlock()
		return nil
	} else if atomic.LoadInt32(&d.len) == 1 {
		if atomic.LoadInt32(&d.len) == 1 {
			//d.taillock.Lock()
			atomic.AddInt32(&d.head, 1)
			data, _ := d.data.Load(d.head)
			d.data.Delete(d.head)
			atomic.AddInt32(&d.len, -1)
			//d.taillock.Unlock()
			d.headlock.Unlock()
			d.zerolock.Unlock()
			return data
		}
		d.zerolock.Unlock()
	}
	d.zerolock.Unlock()
	atomic.AddInt32(&d.head, 1)
	data, _ := d.data.Load(d.head)
	d.data.Delete(d.head)
	atomic.AddInt32(&d.len, -1)
	d.headlock.Unlock()
	return data
}
func (d *deque) PullFromTail() interface{} {
	d.zerolock.Lock()
	d.taillock.Lock()
	if atomic.LoadInt32(&d.len) <= 0 {
		d.taillock.Unlock()
		d.zerolock.Unlock()
		return nil
	} else if atomic.LoadInt32(&d.len) == 1 {
		//d.headlock.Lock()
		atomic.AddInt32(&d.tail, -1)
		data, _ := d.data.Load(d.tail)
		d.data.Delete(d.tail)
		atomic.AddInt32(&d.len, -1)
		//d.headlock.Unlock()
		d.zerolock.Unlock()
		d.taillock.Unlock()
		return data
	}
	d.zerolock.Unlock()
	atomic.AddInt32(&d.tail, -1)
	data, _ := d.data.Load(d.tail)
	d.data.Delete(d.tail)
	atomic.AddInt32(&d.len, -1)
	d.taillock.Unlock()
	return data
}
